package khai.spring;

import khai.spring.annotation.Autowired;
import khai.spring.annotation.Controller;
import khai.spring.annotation.RequestMapping;
import khai.spring.annotation.Service;
import khai.spring.util.XmlConfig;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * 实现 spring ioc 以及 aop?
 *
 * @author Khai Loon
 * @version 2017/4/13 上午 10:55
 */

public class Dispatcher extends HttpServlet {

    private List<String> beaName = new ArrayList<>();
    private Map<String, Object> bean = new HashMap<>();//controller,service 类
    private Map<String, Method> methodMap = new HashMap<>();//url 与方法对应
    private Map<String, Object> urlController = new HashMap<>();// url  与控制器类对应

    @Override
    public void init() {
        try {
            /**  拿到基包信息xml **/
            String config = this.getInitParameter("config");
            String springXml = config.replace("classpath*:", "");
            String packageStr = new XmlConfig().getConfig(springXml);

            /**  全自动扫描将 bean 加载到spring里面 **/
            scanBean(packageStr);

            findBean();

            /**  通过注解 注入实例 **/
            springIoc();

            /**  通过url 找到对应url  处理器 **/
            handlerMapping();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 所有请求经过这里
     */
    @Override
    public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
        process(req, res);
    }

    private void process(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String methodStr = req.getRequestURI();
        Method method = methodMap.get(methodStr);
        if (method != null) {
            Object object = urlController.get(methodStr);
            if (object != null) {
                Object[] args = {req, resp};
                try {
                    System.out.println("调用方法 " + methodStr);
                    method.invoke(object, args);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        } else {
            System.out.println(String.format("方法不存在 %s", methodStr));
        }
    }

    /**
     * 得到 class 全类名
     * khai.spring.ioc.SpringController
     * khai.spring.ioc.SpringService
     *
     * @param packing
     * @throws IOException
     */
    private void scanBean(String packing) throws IOException {
        String packageDirName = packing.replaceAll("\\.", "/");
        Enumeration<URL> urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(packageDirName);

        while (urlEnumeration.hasMoreElements()) {
            String filePath = urlEnumeration.nextElement().getFile();
            String[] files = new File(filePath).list();
            for (String path : files) {
                File eachFile = new File(filePath + "/" + path);

                if (eachFile.isDirectory()) {
                    scanBean(packing + "." + eachFile.getName());
                } else if (eachFile.isFile()) {
                    beaName.add(packing + "." + eachFile.getName());
                    System.out.println("扫描到类 " + packing + "." + eachFile.getName());
                }
            }
        }

    }

    /**
     * 得到 Controller  Service 注解类
     */
    private void findBean() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        if (beaName.isEmpty()) return;

        for (String s : beaName) {
            Class<? extends Object> objClass = Class.forName(s.replace(".class", ""));
            if (objClass.isAnnotationPresent(Controller.class) || objClass.isAnnotationPresent(Service.class)) {
                bean.put(objClass.getSimpleName(), objClass.newInstance());
            }
        }

    }

    /**
     * 初始化 Autowired 标识的变量
     */
    private void springIoc() throws IllegalAccessException {
        if (bean.isEmpty()) {
            throw new RuntimeException("没有可用注解");
        }

        for (Map.Entry<String, Object> entry : bean.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    String type = field.getGenericType().getTypeName();
                    type = type.substring(type.lastIndexOf(".") + 1);
                    field.setAccessible(true);
                    // set(Object obj, Object value) ： 向obj对象的这个Field设置新值value
                    field.set(entry.getValue(), bean.get(type));
                }
            }

        }
    }

    /**
     * 路径映射 到 方法,对象
     */
    private void handlerMapping() {
        if (bean.isEmpty()) {
            throw new RuntimeException("没有可用注解");
        }

        for (Map.Entry<String, Object> entry : bean.entrySet()) {
            if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
                String controllerUrl = entry.getValue().getClass().getAnnotation(RequestMapping.class).value();
                Method[] methods = entry.getValue().getClass().getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        String methodUrl = method.getAnnotation(RequestMapping.class).value();
                        methodMap.put("/" + controllerUrl + "/" + methodUrl, method);
                        urlController.put("/" + controllerUrl + "/" + methodUrl, entry.getValue());
                    }
                }
            }
        }

    }


}
